<template>
  <audio
    ref="audio"
    @loadstart="loading = true"
    @canplay="loading = false"
    @canplaythrough="loading = false"
    @playing="handlePlaying"
    @pause="isPlaying = false"
    @timeupdate="e => second = e.target.currentTime"
    @durationchange="handleDurationChange"
    @ended="handleEnded"
    @error="$emit('error', $event)"
  />
</template>

<script>
  /**
   * 微信音频播放兼容处理组件，URL可以为http链接，wxLocalID，wxServerID
   * （注意：wxLocalID的资源格式在iOS和Android上不同）
   * 事件列表：
   *    domReady: dom就绪触发,传递一个audio的dom节点
   *    beforePlay：播放前触发
   *    loading：资源下载时触发，下载开始时传递一个true，下载完成后传递一个false
   *    durationchange：音频时长更新，传递一个duration,播放音频logo的时候不会触发
   *    togglePlay：播放状态改变时触发，传递一个播放状态参数，true为开始播放，false为停止播放
   *    timeupdate：音频播放读秒，传递一个currentTime,播放音频logo的时候不会触发
   *    ended：播放停止触发
   *    error：发生错误时触发
   */
  import wx from 'weixin-js-sdk'
  import {deviceDetect} from '../utils'

  // 用于在组件销毁时，清除iOS上的touchstart事件监听
  let destroyEventListener;

  // 设备平台判断
  let platform = deviceDetect();

  export default {
    name: "w-x-audio-player",

    props: {
      URL: {
        type: String
      },
      autoPlay: {
        type: Boolean,
      },
      duration: {
        type: Number
      },
      audioHeader: {
        default() {
          return {
            URL: '',
            loop: false
          }
        }
      }
    },

    data() {
      return {
        platform,
        loading: false,
        isPlaying: false,
        second: 0,
        durationFromAudioElmDurationChange: 0,
        WXLocalResource: '',
        // 微信播放计时容器
        tickInterval: null,
        // 微信播放结束
        wxVoicePlayEnd: true,
        ended: false,
        isPlayingAudioHeader: false,
      }
    },

    watch: {
      URL(val) {
        this.$emit('initial');
        this.initial(val);
      },
      duration(val) {
        this.second = val;
      },
      second(val) {
        if(!this.isPlayingAudioHeader) {
          this.$emit('timeupdate', val)
        }
      },
      loading(val) {
        this.$emit('loading', val)
      },
      isPlaying(val) {
        this.$emit('togglePlay', val)
      },
      ended(val) {
        this.$emit('ended', val);
        if (this.audioHeader) {
          if (this.isPlayingAudioHeader) {
            this.isPlayingAudioHeader = false;
            if (this.isHttpResource) {
              this.$refs.audio.src = this.URL;
              this.togglePlay(true);
            }
          } else if (this.isPlayingAudioHeader.loop) {
            this.isPlayingAudioHeader = true;
            this.$refs.audio.src = this.isPlayingAudioHeader.URL
          }
        }
      },
    },

    computed: {
      isHttpResource() {
        return /^http/.test(this.URL)
      },

      isWXLocalResource() {
        return /^wxLocalResource/.test(this.URL)
        || /^weixin:\/\/resourceid/.test(this.URL)
      },

      isWXServiceResource() {
        return !this.isWXLocalResource
          && !this.isHttpResource
          && typeof this.URL === 'string'
          && this.URL.length
      },
    },

    methods: {
      initial(URL) {
        this.stopTick();
        this.$emit('initial');
        if (this.isHttpResource) {
          // 标准资源链接
          this.initialAudioElm(URL)
        } else if (this.isWXLocalResource) {
          // 微信本地资源
          this.WXLocalResource = URL
        } else if (this.isWXServiceResource) {
          // 微信服务器资源
          this.loading = true;
          wx.ready(
            () => wx.downloadVoice({
              serverId: URL, // 需要下载的音频的服务器端ID，由uploadVoice接口获得
              isShowProgressTips: 1, // 默认为1，显示进度提示
              success: ({localId}) => {
                this.WXLocalResource = localId
              },
              fail: ({errMsg}) => {
                this.$ZCAlert.show({
                  title: '音频下载失败',
                  content: errMsg
                })
              },
              complete: () => this.loading = false
            })
          );
        } else {
          this.$emit('error', new Error('无效的资源格式：' + URL))
        }
        if (this.audioHeader.URL) {
          this.$refs.audio.src = this.audioHeader.URL;
        }
        this.autoPlay && this.handleiOS(this.togglePlay);
      },

      handleiOS(fn) {
        const vm = this;

        if(this.platform === 'ios'){
          // iOS音频播放必须放在用户交互事件内
          function cb() {
            fn.call(vm);
            window.removeEventListener('touchstart', cb);
          }

          window.addEventListener('touchstart', cb);
          destroyEventListener = () => window.removeEventListener('touchstart', cb);
        } else {
          fn.call(vm)
        }
      },

      initialAudioElm(URL) {
        const audioElm = this.$refs.audio;

        audioElm.src = URL;
        if (!this.autoPlay) {
          this.handleiOS(function () {
            this.$refs.audio.load()
          })
        }
      },

      handleDurationChange(e) {
        this.second = 0;
        this.durationFromAudioElmDurationChange = e.target.duration;
        if(!this.isPlayingAudioHeader){
          this.$emit('durationchange', e.target.duration)
        }
      },

      togglePlay(play = !this.isPlaying) {
        const audioElm = this.$refs.audio;

        if (play) {
          this.$emit('beforePlay');
          this.ended = false;
        }

        if (
          this.isPlayingAudioHeader
          || this.isHttpResource
        ) {
          return new Promise(function (res, rej) {
            const method = play ? 'play' : 'pause';

            audioElm[`on${method}`] = function () {
              audioElm[`on${method}`] = null;
              res();
            };
            audioElm.onerror = function (err) {
              audioElm.onerror = null;
              rej(err);
            };
            audioElm[method]();
          });
        } else if (this.isWXLocalResource || this.isWXServiceResource) {
          return this[!play ? 'WXPauseVoice' : 'WXPlayVoice']();
        } else {
          return Promise.reject(
            new Error(`无效的音频资源:${String(this.URL)}`)
          )
        }
      },

      play() {
        return !this.isPlaying ?
          this.togglePlay(true)
          : Promise.resolve()
      },

      pause() {
        return this.isPlaying ?
          this.togglePlay(false)
          : Promise.resolve()
      },

      // 微信录音计时
      WXRecordVoiceTime() {
        this.second = 0;
        this.tickTick();
        this.wxVoicePlayEnd = true;
      },

      tickTick() {
        this.tickInterval = setInterval(() => this.second += .5, 500);
      },

      stopTick() {
        this.tickInterval && clearInterval(this.tickInterval);
        this.tickInterval = null;

        return this.second
      },

      WXPlayVoice() {
        return new Promise((res, rej) => {
          setTimeout(() => {
            wx.playVoice({
              localId: this.WXLocalResource,
              success: () => {
                this.wxVoicePlayEnd && (this.second = 0);
                this.wxVoicePlayEnd = false;
                this.tickTick();
                this.isPlaying = true;
                this.WXOnVoicePlayEnd();
                res();
              },
              fail: ({errMsg}) => rej(new Error(errMsg))
            });
          }, 100)
        })
      },

      WXPauseVoice() {
        return new Promise(function (res, rej) {
          wx.pauseVoice({
            localId: this.WXLocalResource,
            success: () => {
              this.isPlaying = false;
              this.stopTick();
              res();
            },
            fail({errMsg}) {
              rej(new Error(errMsg))
            }
          });
        })
      },

      WXOnVoicePlayEnd() {
        wx.onVoicePlayEnd({
          success: () => {
            this.isPlaying = false;
            this.wxVoicePlayEnd = true;
            this.stopTick();
            this.ended = true;
          },
          fail: ({errMsg}) => this.$emit('error', errMsg)
        });
      },

      handlePlaying() {
        this.isPlaying = true;
        this.loading = false;
      },

      // 设置音频进度，接受一个百分比的Number
      setProgress(progress) {
        const duration = this.durationFromAudioElmDurationChange || this.duration;

        if (
          duration
          && this.isHttpResource
        ) {
          this.$refs.audio.currentTime = Math.floor(duration * progress / 100)
        } else {
          console.error(new Error(
            `setProgress fail: Resource is${this.isHttpResource ? '' : 'n\'t'}URL`
          ))
        }
      },

      handleEnded() {
        this.ended = true;
      }
    },

    mounted() {
      if (typeof this.URL !== 'undefined') {
        if (this.audioHeader.URL) {
          this.isPlayingAudioHeader = true;
        }
        this.initial(this.URL);
      }
      this.$emit('domReady', this.$refs.audio)
    },

    beforeDestroy() {
      destroyEventListener && destroyEventListener();
    }
  }
</script>
